#pragma once

#include <string>

#include <sys/epoll.h>

#include "Socket.hpp"

using namespace std;
using namespace SocketModule;

class EpollServer
{
    static const int default_recv_size = 1024;

    static const int default_wait_max_size = 64; // 调用一次epoll_wait最多可以获取多少个就绪fd

    static const int default_fd = -1;

public:
    EpollServer(uint16_t port)
        : _listensocket(make_shared<TcpSocket>(port)),
          _port(port),
          _isrunning(false),
          _epollfd(default_fd)
    {
        // 初始化listensocket
        _listensocket->BuildSocket(SockType::LISTEN_SOCK);

        // 创建epoll模型
        int n = epoll_create(256);
        if (n > 0)
        {
            LOG(LogLevel::DEBUG) << "epoll模型创建完成,epollfd: " << n;
            _epollfd = n;
        }
        else
        {
            LOG(LogLevel::ERROR) << "epoll_create error";
            exit(1);
        }

        // 把listensocket对应的fd添加到epoll模型中，并进行读事件关心
        struct epoll_event event;
        event.events = EPOLLIN; // 读事件关心
        event.data.fd = _listensocket->GetSockfd();
        n = epoll_ctl(_epollfd, EPOLL_CTL_ADD, _listensocket->GetSockfd(), &event);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "把listensocketfd添加到epoll模型中失败";
            exit(2);
        }
    }
    void Start()
    {
        if (_isrunning == true)
        {
            return;
        }
        _isrunning = true;
        while (true)
        {
            // 阻塞等待fd们就绪
            int n = epoll_wait(_epollfd, _events, default_wait_max_size, -1);
            if (n > 0) // 有fd就绪
            {
                // 遍历处理所有就绪的fd
                for (int i = 0; i < n; i++)
                {
                    // 如果是listensocketfd就绪
                    if (_events[i].data.fd == _listensocket->GetSockfd())
                    {
                        // 判断一下就绪事件的类型
                        if (_events[i].events & EPOLLIN) // 如果是读事件就绪
                        {
                            // 去获取新连接
                            LOG(LogLevel::DEBUG) << "listensocketfd: " << _events[i].data.fd << "读事件就绪";

                            Accept();
                        }
                    }
                    else // 如果是普通fd就绪
                    {
                        // 判断一下就绪事件的类型
                        if (_events[i].events & EPOLLIN) // 如果是读事件就绪
                        {
                            // 去读取用户请求
                            LOG(LogLevel::DEBUG) << "fd: " << _events[i].data.fd << "读事件就绪";

                            Recv(&_events[i]);
                        }
                        else if (_events[i].events & EPOLLOUT) // 如果是写事件就绪
                        {
                            //...
                        }
                    }
                }
            }
            else if (n == 0)
            {
                LOG(LogLevel::ERROR) << "没有fd就绪";
            }
            else
            {
                LOG(LogLevel::ERROR) << "epoll_wait error";
            }
        }
        _isrunning = false;
    }
    void Accept()
    {
        // 获取新链接
        uint32_t ip_client;
        uint16_t port_client;
        SockPtr newsock = _listensocket->Accept(ip_client, port_client);
        if(newsock==nullptr)
        {
            LOG(LogLevel::ERROR) << "accept error";
            return;
        }
        int newsockfd = newsock->GetSockfd();

        //把新连接的sockfd添加到epoll模型中
        struct epoll_event event;
        event.events = EPOLLIN; // 读事件关心

        event.data.fd = newsockfd;

        int n = epoll_ctl(_epollfd, EPOLL_CTL_ADD,newsockfd, &event);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "把新连接的sockfd添加到epoll模型中失败";
        }
        else
        {
            LOG(LogLevel::DEBUG) << "成功把新连接的sockfd: "<<newsockfd<<"添加到epoll模型";
        }
    }
    void Recv(struct epoll_event* event)
    {
        char rbuf[default_recv_size];
        // 开始读取请求
        int rval = recv(event->data.fd, rbuf, sizeof(rbuf), 0);
        if (rval > 0)
        {
            rbuf[rval] = '\0';
            LOG(LogLevel::DEBUG) << "客户端发来信息，fd: " << event->data.fd << "#  " << rbuf;

            string out = "server# ";
            out += rbuf;
            // 回复客户端
            send(event->data.fd, out.c_str(), out.size(), 0);
        }
        else if (rval == 0)
        {
            LOG(LogLevel::DEBUG) << "客户端断开连接，fd: " << event->data.fd;

            //把对应的fd从epoll模型中删除
            epoll_ctl(_epollfd,EPOLL_CTL_DEL,event->data.fd,nullptr);
            //关闭不需要的fd
            close(event->data.fd);//注意：不能先关闭fd，再epoll_ctl
        }
        else
        {
            LOG(LogLevel::ERROR) << "recv error";
        }
    }
    void Stop()
    {
        _isrunning = false;
    }
    ~EpollServer()
    {
        _listensocket->Close();
    }

private:
    uint16_t _port;
    SockPtr _listensocket;
    int _epollfd;
    struct epoll_event _events[default_wait_max_size];
    bool _isrunning;
};